#pragma once 
#include "IDoubleMatrix.h"

namespace LatoolNet {

	using namespace System;
	using namespace System::Text;
	using namespace System::Collections::Generic;

	///
	///<summary>
	/// This class stands for formatting information of matrix.
	///</summary>
	///
	public ref class MatrixFormatter {
	private:

		String^ m_lineBeginsWith;
		String^ m_lineEndsWith;
		String^ m_elementSeparatedBy;
		int m_numberOfDigit;
		String^ m_complexBeginsWith;
		String^ m_complexEndsWith;
		String^ m_complexValueSeparatedBy;

	internal:

		static String^ FormatStringByLine(IDoubleMatrix^ mat, MatrixFormatter^ formatter, int rowIndex) {
			StringBuilder ^ builder = gcnew StringBuilder();
			builder->Append(formatter->LineBeginsWith);
			for (int j = 0; j < mat->ColNum; j++) {
				if (formatter->NumberOfDigit > 0) {
					builder->Append(String::Format("{0:f" + formatter->NumberOfDigit + "}", mat[rowIndex, j]));
				} else {
					builder->Append(mat[rowIndex, j].ToString());
				}
				if (j < mat->ColNum -1) {
					builder->Append(formatter->ElementSeparatedBy);
				}
			}
			builder->Append(formatter->LineEndsWith);
			return builder->ToString();
		};

		static String^ FormatStringByLine(IComplexMatrix^ mat, MatrixFormatter^ formatter, int rowIndex) {
			StringBuilder ^ builder = gcnew StringBuilder();
			builder->Append(formatter->LineBeginsWith);
			for (int j = 0; j < mat->ColNum; j++) {
				builder->Append(formatter->ComplexBeginsWith);
				if (formatter->NumberOfDigit > 0) {
					builder->Append(String::Format("{0:f" + formatter->NumberOfDigit + "}", mat[rowIndex, j].Real));
				} else {
					builder->Append(mat[rowIndex, j].Real.ToString());
				}
				builder->Append(formatter->ComplexValueSepratedBy);
				if (formatter->NumberOfDigit > 0) {
					builder->Append(String::Format("{0:f" + formatter->NumberOfDigit + "}", mat[rowIndex, j].Imag));
				} else {
					builder->Append(mat[rowIndex, j].Imag.ToString());
				}
				builder->Append(formatter->ComplexEndsWith);
				if (j < mat->ColNum -1) {
					builder->Append(formatter->ElementSeparatedBy);
				}
			}
			builder->Append(formatter->LineEndsWith);
			return builder->ToString();
		};

		static String^ FormatString(IDoubleMatrix^ mat, MatrixFormatter^ formatter) {
			StringBuilder ^ builder = gcnew StringBuilder();
			for (int i = 0; i < mat->RowNum; i++) {
				builder->AppendLine(FormatStringByLine(mat, formatter, i));		
			}
			return builder->ToString();
		};

		static String^ FormatString(IComplexMatrix^ mat, MatrixFormatter^ formatter) {
			StringBuilder ^ builder = gcnew StringBuilder();
			for (int i = 0; i < mat->RowNum; i++) {
				builder->AppendLine(FormatStringByLine(mat, formatter, i));		
			}
			return builder->ToString();
		};

		static array<double>^ ParseLine(String^ line, MatrixFormatter^ formatter) {
			String ^ replacedLine = line->Replace(formatter->LineBeginsWith, "")->Replace(formatter->LineEndsWith, "");
			array<String^>^ tokens = replacedLine->Split(formatter->ElementSeparatedBy->ToCharArray());
			List<double>^ ret = gcnew List<double>();
			for (int i = 0; i < tokens->Length; i++) {
				if (tokens[i]->Trim()->Equals("")) {
					continue;
				}
				ret->Add(Double::Parse(tokens[i]));
			}
			return ret->ToArray();
		};

		static array<Complex>^ ParseComplexLine(String^ line, MatrixFormatter^ formatter) {
			String ^ replacedLine = line->Replace(formatter->LineBeginsWith, "")->Replace(formatter->LineEndsWith, "")->Replace(formatter->ElementSeparatedBy, " ");
			array<String^>^ tokens = replacedLine->Split(formatter->ComplexEndsWith->ToCharArray());
			List<Complex>^ ret = gcnew List<Complex>();
			for (int i = 0; i < tokens->Length; i++) {
				if (tokens[i]->Trim()->Equals("")) {
					continue;
				}
				String^ complexStr = tokens[i]->Replace(formatter->ComplexBeginsWith, "")->Replace(formatter->ComplexValueSepratedBy, " ");
				String^ sep = " ";
				array<String^>^ complexTokens = complexStr->Trim()->Split(sep->ToCharArray());
				String^ realStr = complexTokens[0];
				String^ imagStr = complexTokens[1];

				double real = Double::Parse(realStr);
				double imag = Double::Parse(imagStr);

				ret->Add(Complex(real, imag));
			}
			return ret->ToArray();
		};

	public:
		MatrixFormatter() {
			m_lineBeginsWith = "[";
			m_lineEndsWith = "]";
			m_elementSeparatedBy = ",";
			m_numberOfDigit = 4;
			m_complexBeginsWith = "(";
			m_complexEndsWith = ")";
			m_complexValueSeparatedBy = ",";
		};

		property int NumberOfDigit {
			int get() { return m_numberOfDigit; }
			void set(int value) { m_numberOfDigit = value; }
		};

		property String^ LineBeginsWith {
			String^ get() { return m_lineBeginsWith; }
			void set(String^ value) { m_lineBeginsWith = value; }

		};

		property String^ LineEndsWith {
			String^ get() { return m_lineEndsWith; }
			void set(String^ value) { m_lineEndsWith = value; }

		};

		property String^ ElementSeparatedBy {
			String^ get() { return m_elementSeparatedBy; }
			void set(String^ value) { m_elementSeparatedBy = value; }

		};

		property String^ ComplexBeginsWith {
			String^ get() { return m_complexBeginsWith; }
			void set(String^ value) { m_complexBeginsWith = value; }

		};

		property String^ ComplexEndsWith {
			String^ get() { return m_complexEndsWith; }
			void set(String^ value) { m_complexEndsWith = value; }

		};

		property String^ ComplexValueSepratedBy {
			String^ get() { return m_complexValueSeparatedBy; }
			void set(String^ value) { m_complexValueSeparatedBy = value; }

		};
	};
}